From 238daefdd2c366f86e466f552c00cd92d40a31ef Mon Sep 17 00:00:00 2001 From: "smh22@boulderdash.cl.cam.ac.uk" Date: Fri, 14 Feb 2003 14:42:14 +0000 Subject: [PATCH] bitkeeper revision 1.33.1.1 (3e4d0046UBuDSsmiQzTssLuipi89Wg) put domain builder tools and scripts under bk (these are for running under domain 0) --- .rootkeys | 11 + tools/domain_builder/Makefile | 17 + tools/domain_builder/README | 29 ++ tools/domain_builder/dom0_defs.h | 8 + tools/domain_builder/dom0_ops.h | 80 ++++ tools/domain_builder/dom_builder.c | 485 +++++++++++++++++++++++++ tools/domain_builder/dom_kill.c | 55 +++ tools/domain_builder/hypervisor_defs.h | 36 ++ tools/domain_builder/mem_defs.h | 45 +++ tools/domain_builder/newdom | 17 + tools/domain_builder/vifinit | 25 ++ xen-2.4.16/arch/i386/apic.o | Bin 0 -> 58292 bytes 12 files changed, 808 insertions(+) create mode 100644 tools/domain_builder/Makefile create mode 100644 tools/domain_builder/README create mode 100644 tools/domain_builder/dom0_defs.h create mode 100644 tools/domain_builder/dom0_ops.h create mode 100644 tools/domain_builder/dom_builder.c create mode 100644 tools/domain_builder/dom_kill.c create mode 100644 tools/domain_builder/hypervisor_defs.h create mode 100644 tools/domain_builder/mem_defs.h create mode 100644 tools/domain_builder/newdom create mode 100644 tools/domain_builder/vifinit create mode 100644 xen-2.4.16/arch/i386/apic.o diff --git a/.rootkeys b/.rootkeys index fbeff2f687..7fbe8db286 100644 --- a/.rootkeys +++ b/.rootkeys @@ -1,12 +1,23 @@ 3ddb6b0bKlMz_dz-M59a1mkUa1lASw BitKeeper/etc/config 3ddb6b0buTaC5zg1_a8FoAR9FWi_mw BitKeeper/etc/ignore 3ddb79c9_hgSp-gsQm8HqWM_9W3B_A BitKeeper/etc/logging_ok +3e4d00468-FN2VDeEHo96zxrMHK_mA tools/domain_builder/Makefile +3e4d0046SPau_y0sw2WLJz8QkqNoRA tools/domain_builder/README +3e4d0046bbdH0GsI9J_1Eb4ZQHfIiQ tools/domain_builder/dom0_defs.h +3e4d0046RgYCfGOw6qGz_7kYLMV2Vw tools/domain_builder/dom0_ops.h +3e4d0046ouLij_CMN_j7-dUHZIBI_A tools/domain_builder/dom_builder.c +3e4d0046EKs06fY0CWDEgZQcn7DYUg tools/domain_builder/dom_kill.c +3e4d0046aPbGiRTtdWxqY5b3ytWurA tools/domain_builder/hypervisor_defs.h +3e4d00468aE86IfyjfrJwYoxzM7pAw tools/domain_builder/mem_defs.h +3e4d0046VHhXwFuG5FK34AVxqd5A_A tools/domain_builder/newdom +3e4d0046IBzDIeaMbQB-e2QB2ahbig tools/domain_builder/vifinit 3ddb79bcbOVHh38VJzc97-JEGD4dJQ xen-2.4.16/Makefile 3ddb79bcCa2VbsMp7mWKlhgwLQUQGA xen-2.4.16/README 3ddb79bcWnTwYsQRWl_PaneJfa6p0w xen-2.4.16/Rules.mk 3ddb79bcZbRBzT3elFWSX7u6NtMagQ xen-2.4.16/arch/i386/Makefile 3ddb79bcBQF85CfLS4i1WGZ4oLLaCA xen-2.4.16/arch/i386/Rules.mk 3ddb79bcsjinG9k1KcvbVBuas1R2dA xen-2.4.16/arch/i386/apic.c +3e4d0046IHluXnd2lSLBCKqMg2QsZg xen-2.4.16/arch/i386/apic.o 3ddb79bcSC_LvnmFlX-T5iTgaR0SKg xen-2.4.16/arch/i386/boot/boot.S 3ddb79bcUrk2EIaM5VsT6wUudH1kkg xen-2.4.16/arch/i386/delay.c 3ddb79bcecupHj56ZbTa3B0FxDowMg xen-2.4.16/arch/i386/entry.S diff --git a/tools/domain_builder/Makefile b/tools/domain_builder/Makefile new file mode 100644 index 0000000000..6707481f1b --- /dev/null +++ b/tools/domain_builder/Makefile @@ -0,0 +1,17 @@ +CC = gcc +BUILDER = domain_builder +KILL = kill_domain + +all: dom_builder.o dom_kill.o + $(CC) -o $(BUILDER) dom_builder.o + $(CC) -o $(KILL) dom_kill.o + +dom_builder.o: dom_builder.c dom0_defs.h dom0_ops.h hypervisor_defs.h mem_defs.h + $(CC) -c dom_builder.c + +dom_kill.o: dom_kill.c dom0_ops.h dom0_defs.h + $(CC) -c dom_kill.c + +clean: + $(RM) *.o domain_builder kill_domain + diff --git a/tools/domain_builder/README b/tools/domain_builder/README new file mode 100644 index 0000000000..7622eada5d --- /dev/null +++ b/tools/domain_builder/README @@ -0,0 +1,29 @@ +A couple of simple steps to get you going: + +1. do make (suitable Makefile is in the source dir) +2. copy over andy's vifinit script to local dir +3. copy over xenolinux guestos image (NB. image needs to be uncompressed, so +if the only image you have is image.gz do gunzip image.gz before doing +anything further) +4. add executable permissions to newdom and vifint +5. edit newdom script and change it as it suits you + +newdom script takes guestos image file name as first parameter and newdom id +as second parameter, eg.: + +./newdom xenolinux 1 + +should initiate building of dom1 with the image contained in file named +xenolinux in local dir. + +in general, domain_builder application takes three parameters: requested +memory in kb, guestos image file name and number of vifs to be created, eg. + +./domain_builder 16000 xenolinux 1 + +would build domX reserving 16MB mem, creating 1 vif and using os image stored +as xenolinux in local dir. + +happy booting! + +boris diff --git a/tools/domain_builder/dom0_defs.h b/tools/domain_builder/dom0_defs.h new file mode 100644 index 0000000000..bba020470f --- /dev/null +++ b/tools/domain_builder/dom0_defs.h @@ -0,0 +1,8 @@ +#define PROC_XENO_ROOT "xeno" +#define PROC_CMD "dom0_cmd" +#define PROC_DOM_PREFIX "dom" +#define PROC_DOM_MEM "mem" +#define PROC_DOM_DATA "new_dom_data" + +#define MAX_PATH 256 + diff --git a/tools/domain_builder/dom0_ops.h b/tools/domain_builder/dom0_ops.h new file mode 100644 index 0000000000..d98ce1b1eb --- /dev/null +++ b/tools/domain_builder/dom0_ops.h @@ -0,0 +1,80 @@ +/****************************************************************************** + * dom0_ops.h + * + * Process command requests from domain-0 guest OS. + * + * Copyright (c) 2002, K A Fraser, B Dragovic + */ + +#define DOM0_NEWDOMAIN 0 +#define DOM0_KILLDOMAIN 1 +#define DOM0_GETMEMLIST 2 +#define DOM0_STARTDOM 4 +#define MAP_DOM_MEM 6 /* Not passed down to Xen */ +#define DO_PGUPDATES 7 /* Not passed down to Xen */ +#define MAX_CMD 8 + +#define MAX_CMD_LEN 256 + +typedef struct dom0_newdomain_st +{ + unsigned int domain; + unsigned int memory_kb; + unsigned int num_vifs; // temporary + unsigned long pg_head; // return parameter +} dom0_newdomain_t; + +typedef struct dom0_killdomain_st +{ + unsigned int domain; +} dom0_killdomain_t; + +typedef struct dom0_getmemlist_st +{ + unsigned long start_pfn; + unsigned long num_pfns; + void *buffer; +} dom0_getmemlist_t; + +/* This is entirely processed by XenoLinux */ +typedef struct dom_mem +{ + unsigned int domain; + unsigned long vaddr; + unsigned long start_pfn; + int tot_pages; +} dom_mem_t; + +/* This is entirely processed by XenoLinux */ +typedef struct dom_pgupdate +{ + unsigned long pgt_update_arr; + unsigned long num_pgt_updates; +} dom_pgupdate_t; + +typedef struct domain_launch +{ + unsigned int domain; + unsigned long l2_pgt_addr; + unsigned long virt_load_addr; + unsigned long virt_shinfo_addr; + unsigned long virt_startinfo_addr; + unsigned int num_vifs; + char cmd_line[MAX_CMD_LEN]; +} dom_meminfo_t; + +typedef struct dom0_op_st +{ + unsigned long cmd; + union + { + dom0_newdomain_t newdomain; + dom0_killdomain_t killdomain; + dom0_getmemlist_t getmemlist; + dom_mem_t dommem; + dom_pgupdate_t pgupdate; + dom_meminfo_t meminfo; + } + u; +} dom0_op_t; + diff --git a/tools/domain_builder/dom_builder.c b/tools/domain_builder/dom_builder.c new file mode 100644 index 0000000000..4e39068686 --- /dev/null +++ b/tools/domain_builder/dom_builder.c @@ -0,0 +1,485 @@ +/* + * XenoDomainBuilder, copyright (c) Boris Dragovic, bd240@cl.cam.ac.uk + * This code is released under terms and conditions of GNU GPL :). + * Usage: + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "hypervisor_defs.h" +#include "dom0_ops.h" +#include "dom0_defs.h" +#include "mem_defs.h" + +#define PERR_STRING "Xeno Domain Builder" + +#define GUEST_SIG "XenoGues" +#define SIG_LEN 8 + +#define L1_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED) +#define L2_PROT (_PAGE_PRESENT|_PAGE_RW|_PAGE_USER|_PAGE_ACCESSED|_PAGE_DIRTY) + +/* standardized error reporting function */ +static void dberr(char *msg) +{ + printf("%s: %s\n", PERR_STRING, msg); +} + +/* status reporting function */ +static void dbstatus(char * msg) +{ + printf("Domain Builder: %s\n", msg); +} + +/* clean up domain's memory allocations */ +static void dom_mem_cleanup(dom_mem_t * dom_mem) +{ + char mem_path[MAX_PATH]; + int mem_fd; + + /* open the domain's /proc mem interface */ + sprintf(mem_path, "%s%s%s%s%d%s%s", "/proc/", PROC_XENO_ROOT, "/", + PROC_DOM_PREFIX, dom_mem->domain, "/", PROC_DOM_MEM); + + mem_fd = open(mem_path, O_WRONLY); + if(mem_fd < 0){ + perror(PERR_STRING); + } + + if(write(mem_fd, (dom_mem_t *)dom_mem, sizeof(dom_mem_t)) < 0){ + dbstatus("Error unmapping domain's memory.\n"); + } + + close(mem_fd); +} + +/* ask dom0 to export domains memory through /proc */ +static int setup_dom_memmap(unsigned long pfn, int pages, int dom) +{ + char cmd_path[MAX_PATH]; + dom0_op_t dop; + int cmd_fd; + + dop.cmd = MAP_DOM_MEM; + dop.u.dommem.start_pfn = pfn; + dop.u.dommem.tot_pages = pages; + dop.u.dommem.domain = dom; + + /* open the /proc command interface */ + sprintf(cmd_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", PROC_CMD); + cmd_fd = open(cmd_path, O_WRONLY); + if(cmd_fd < 0){ + perror(PERR_STRING); + return -1; + } + + write(cmd_fd, &dop, sizeof(dom0_op_t)); + close(cmd_fd); + + return 0; +} + +/* request the actual mapping from dom0 */ +static unsigned long get_vaddr(unsigned int dom) +{ + char mem_path[MAX_PATH]; + unsigned long addr; + int mem_fd; + + /* open the domain's /proc mem interface */ + sprintf(mem_path, "%s%s%s%s%d%s%s", "/proc/", PROC_XENO_ROOT, "/", + PROC_DOM_PREFIX, dom, "/", PROC_DOM_MEM); + + mem_fd = open(mem_path, O_RDONLY); + if(mem_fd < 0){ + perror(PERR_STRING); + return 0; + } + + /* get virtual address of mapped region */ + read(mem_fd, &addr, sizeof(addr)); + + close(mem_fd); + + return addr; +} + +static int map_dom_mem(unsigned long pfn, int pages, int dom, + dom_mem_t * dom_mem) +{ + + if(setup_dom_memmap(pfn, pages, dom)){ + perror(PERR_STRING); + return -1; + } + + dom_mem->domain = dom; + dom_mem->start_pfn = pfn; + dom_mem->tot_pages = pages; + if((dom_mem->vaddr = get_vaddr(dom)) == 0){ + dberr("Error mapping dom memory."); + return -1; + } + + return 0; +} + +/* create new domain */ +static dom0_newdomain_t * create_new_domain(long req_mem) +{ + dom0_newdomain_t * dom_data; + char cmd_path[MAX_PATH]; + char dom_id_path[MAX_PATH]; + dom0_op_t dop; + int cmd_fd; + int id_fd; + + /* open the /proc command interface */ + sprintf(cmd_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", PROC_CMD); + cmd_fd = open(cmd_path, O_WRONLY); + if(cmd_fd < 0){ + perror(PERR_STRING); + return 0; + } + + dop.cmd = DOM0_NEWDOMAIN; + dop.u.newdomain.memory_kb = req_mem; + + write(cmd_fd, &dop, sizeof(dom0_op_t)); + close(cmd_fd); + + sprintf(dom_id_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", + PROC_DOM_DATA); + while((id_fd = open(dom_id_path, O_RDONLY)) < 0){} + dom_data = (dom0_newdomain_t *)malloc(sizeof(dom0_newdomain_t)); + read(id_fd, dom_data, sizeof(dom0_newdomain_t)); + close(id_fd); + + sprintf(cmd_path, "Reserved %ld kbytes memory and assigned id %d to the" + "new domain.", req_mem, dom_data->domain); + dbstatus(cmd_path); + + return dom_data; +} + +/* open kernel image and do some sanity checks */ +static int do_kernel_chcks(char *image, long dom_size, + unsigned long * load_addr, size_t * ksize) +{ + char signature[8]; + char status[MAX_PATH]; + struct stat stat; + int fd; + int ret; + + fd = open(image, O_RDONLY); + if(fd < 0){ + perror(PERR_STRING); + ret = -1; + goto out; + } + + if(fstat(fd, &stat) < 0){ + perror(PERR_STRING); + ret = -1; + close(fd); + goto out; + } + + if(stat.st_size > (dom_size << 10)){ + sprintf(status, "Kernel image size %ld larger than requested " + "domain size %ld\n Terminated.\n", stat.st_size, dom_size); + dberr(status); + ret = -1; + close(fd); + goto out; + } + *ksize = stat.st_size - SIG_LEN; + + read(fd, signature, SIG_LEN); + if(strncmp(signature, GUEST_SIG, SIG_LEN)){ + dberr("Kernel image does not contain required signature. " + "Terminating.\n"); + ret = -1; + close(fd); + goto out; + } + + read(fd, load_addr, sizeof(unsigned long)); + + sprintf(status, "Kernel image %s valid, kernel virtual load address %lx", + image, *load_addr); + dbstatus(status); + + ret = fd; + +out: + return ret; +} + +/* this is the main guestos setup function, + * returnes domain descriptor structure to be used when launching + * the domain by hypervisor to do some last minute initialization. + * page table initialization is done by making a list of page table + * requests that are handeled by the hypervisor in the ordinary + * manner. this way, many potentially messy things are avoided... + */ +static dom_meminfo_t * setup_guestos(int dom, int kernel_fd, + unsigned long virt_load_addr, size_t ksize, dom_mem_t *dom_mem) +{ + dom_meminfo_t * meminfo = (dom_meminfo_t *)malloc(sizeof(dom_meminfo_t)); + unsigned long * page_array = (unsigned long *)(dom_mem->vaddr); + page_update_request_t * pgt_updates = (page_update_request_t *) + (dom_mem->vaddr + ((ksize + (PAGE_SIZE-1)) & PAGE_MASK)); + dom_mem_t mem_map; + dom_meminfo_t * ret = NULL; + int alloc_index = dom_mem->tot_pages - 1, num_pt_pages; + unsigned long l2tab; + unsigned long l1tab = 0; + unsigned long num_pgt_updates = 0; + unsigned long pgt_update_arr = (unsigned long)pgt_updates; + unsigned long count, pt_start; + + /* Count bottom-level PTs. Round up to a whole PT. */ + num_pt_pages = + (l1_table_offset(virt_load_addr) + dom_mem->tot_pages + 1023) / 1024; + /* We must also count the page directory. */ + num_pt_pages++; + + /* Index of first PT page. */ + pt_start = dom_mem->tot_pages - num_pt_pages; + + /* first allocate page for page dir. allocation goes backwards from the + * end of the allocated physical address space. + */ + l2tab = *(page_array + alloc_index) << PAGE_SHIFT; + alloc_index--; + meminfo->l2_pgt_addr = l2tab; + meminfo->virt_shinfo_addr = virt_load_addr + nr_2_page(dom_mem->tot_pages); + count = ((unsigned long)pgt_updates - (unsigned long)(dom_mem->vaddr)) + >> PAGE_SHIFT; + + /* zero out l2 page */ + if(map_dom_mem(l2tab >> PAGE_SHIFT, 1, dom_mem->domain, &mem_map)){ + dberr("Unable to map l2 page into Domain Builder."); + goto out; + } + memset((void *)mem_map.vaddr, 0, PAGE_SIZE); + dom_mem_cleanup(&mem_map); + + /* pin down l2tab addr as page dir page - causes hypervisor to provide + * correct protection for the page + */ + pgt_updates->ptr = l2tab | PGREQ_EXTENDED_COMMAND; + pgt_updates->val = PGEXT_PIN_L2_TABLE; + pgt_updates++; + num_pgt_updates++; + + /* this loop initializes page tables and does one extra entry + * to be used by the shared info page. shared info is not in + * the domains physical address space and is not owned by the + * domain. + */ + l2tab += l2_table_offset(virt_load_addr) * sizeof(l2_pgentry_t); + for(count = 0; + count < dom_mem->tot_pages + 1; + count++){ + + if(!((unsigned long)l1tab & (PAGE_SIZE-1))){ + l1tab = *(page_array + alloc_index) << PAGE_SHIFT; + alloc_index--; + + /* zero out l1 page */ + if(map_dom_mem(l1tab >> PAGE_SHIFT, 1, dom_mem->domain, &mem_map)){ + dberr("Unable to map l1 page into Domain Builder."); + goto out; + } + memset((void *)mem_map.vaddr, 0, PAGE_SIZE); + dom_mem_cleanup(&mem_map); + + l1tab += l1_table_offset(virt_load_addr + nr_2_page(count)) + * sizeof(l1_pgentry_t); + + /* make apropriate entry in the page directory */ + pgt_updates->ptr = l2tab; + pgt_updates->val = l1tab | L2_PROT; + pgt_updates++; + num_pgt_updates++; + l2tab += sizeof(l2_pgentry_t); + } + + if ( count < pt_start ) + { + pgt_updates->ptr = l1tab; + pgt_updates->val = (*(page_array + count) << PAGE_SHIFT) | L1_PROT; + pgt_updates++; + num_pgt_updates++; + l1tab += sizeof(l1_pgentry_t); + } + else + { + pgt_updates->ptr = l1tab; + pgt_updates->val = + ((*(page_array + count) << PAGE_SHIFT) | L1_PROT) & ~_PAGE_RW; + pgt_updates++; + num_pgt_updates++; + l1tab += sizeof(l1_pgentry_t); + } + + pgt_updates->ptr = + (*(page_array + count) << PAGE_SHIFT) | PGREQ_MPT_UPDATE; + pgt_updates->val = count; + pgt_updates++; + num_pgt_updates++; + } + + meminfo->virt_startinfo_addr = virt_load_addr + nr_2_page(alloc_index - 1); + meminfo->domain = dom; + + /* copy the guest os image */ + if(!(read(kernel_fd, (char *)dom_mem->vaddr, ksize) > 0)){ + dberr("Error reading kernel image, could not" + " read the whole image. Terminating.\n"); + goto out; + } + + { + dom0_op_t pgupdate_req; + char cmd_path[MAX_PATH]; + int cmd_fd; + + sprintf(cmd_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", PROC_CMD); + if ( (cmd_fd = open(cmd_path, O_WRONLY)) < 0 ) goto out; + + pgupdate_req.cmd = DO_PGUPDATES; + pgupdate_req.u.pgupdate.pgt_update_arr = pgt_update_arr; + pgupdate_req.u.pgupdate.num_pgt_updates = num_pgt_updates; + + write(cmd_fd, &pgupdate_req, sizeof(dom0_op_t)); + close(cmd_fd); + } + + ret = meminfo; +out: + + return ret; +} + +static int launch_domain(dom_meminfo_t * meminfo) +{ + char cmd_path[MAX_PATH]; + dom0_op_t dop; + int cmd_fd; + + sprintf(cmd_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", PROC_CMD); + cmd_fd = open(cmd_path, O_WRONLY); + if(cmd_fd < 0){ + perror(PERR_STRING); + return -1; + } + + dop.cmd = DOM0_STARTDOM; + memcpy(&dop.u.meminfo, meminfo, sizeof(dom_meminfo_t)); + write(cmd_fd, &dop, sizeof(dom0_op_t)); + + dbstatus("Launched the new domain!"); + + close(cmd_fd); +} + +int main(int argc, char **argv) +{ + + dom0_newdomain_t * dom_data; + dom_mem_t dom_os_image; + dom_mem_t dom_pgt; + dom_meminfo_t * meminfo; + size_t ksize; + unsigned long load_addr; + char status[1024]; + int kernel_fd; + int count; + int cmd_len; + int ret = 0; + + unsigned long addr; + + if(argc < 4){ + dberr("Usage: dom_builder " + "\n"); + ret = -1; + goto out; + } + + /* create new domain and set up all the neccessary mappings */ + + kernel_fd = do_kernel_chcks(argv[2], atol(argv[1]), &load_addr, &ksize); + if(kernel_fd < 0){ + ret = -1; + goto out; + } + + /* request the creation of new domain */ + dom_data = create_new_domain(atol(argv[1])); + if(dom_data == 0){ + ret = -1; + goto out; + } + + /* map domain's memory */ + if(map_dom_mem(dom_data->pg_head, dom_data->memory_kb >> (PAGE_SHIFT - 10), + dom_data->domain, &dom_os_image)){ + ret = -1; + goto out; + } + + /* the following code does the actual domain building */ + meminfo = setup_guestos(dom_data->domain, kernel_fd, load_addr, ksize, + &dom_os_image); + if(meminfo == NULL){ + printf("Domain Builder: debug: meminfo NULL\n"); + ret = -1; + dom_mem_cleanup(&dom_os_image); + goto out; + } + + dom_mem_cleanup(&dom_os_image); + + meminfo->virt_load_addr = load_addr; + meminfo->num_vifs = atoi(argv[3]); + meminfo->cmd_line[0] = '\0'; + cmd_len = 0; + for(count = 4; count < argc; count++){ + if(cmd_len + strlen(argv[count]) > MAX_CMD_LEN - 1){ + dberr("Size of image boot params too big!\n"); + break; + } + strcat(meminfo->cmd_line, argv[count]); + strcat(meminfo->cmd_line, " "); + cmd_len += strlen(argv[count] + 1); + } + + sprintf(status, + "About to launch new domain %d with folowing parameters:\n" + " * page table base: %lx \n * load address: %lx \n" + " * shared info address: %lx \n * start info address: %lx \n" + " * number of vifs: %d \n * cmd line: %s \n", meminfo->domain, + meminfo->l2_pgt_addr, meminfo->virt_load_addr, + meminfo->virt_shinfo_addr, meminfo->virt_startinfo_addr, + meminfo->num_vifs, meminfo->cmd_line); + dbstatus(status); + + /* and launch the domain */ + if(launch_domain(meminfo) != 0) + ret = -1; + + free(meminfo); +out: + return ret; +} diff --git a/tools/domain_builder/dom_kill.c b/tools/domain_builder/dom_kill.c new file mode 100644 index 0000000000..2b8b0a5097 --- /dev/null +++ b/tools/domain_builder/dom_kill.c @@ -0,0 +1,55 @@ +/* + * A very(!) simple program to kill a domain. (c) Boris Dragovic + * Usage: + */ + +#include +#include +#include +#include +#include +#include + +#include "dom0_ops.h" +#include "dom0_defs.h" + +#define PERR_STRING "Xen Domain Killer" + +static int do_kill_domain(int dom_id) +{ + char cmd_path[MAX_PATH]; + dom0_op_t dop; + int cmd_fd; + + dop.cmd = DOM0_KILLDOMAIN; + dop.u.killdomain.domain = dom_id; + + /* open the /proc command interface */ + sprintf(cmd_path, "%s%s%s%s", "/proc/", PROC_XENO_ROOT, "/", PROC_CMD); + cmd_fd = open(cmd_path, O_WRONLY); + if(cmd_fd < 0){ + perror(PERR_STRING); + return -1; + } + + write(cmd_fd, &dop, sizeof(dom0_op_t)); + close(cmd_fd); + + return 0; +} + +int main(int argc, char **argv) +{ + int ret; + + if(argc < 2){ + printf("Usage: kill_domain \n"); + ret = -1; + goto out; + } + + ret = do_kill_domain(atoi(argv[1])); + +out: + return ret; +} diff --git a/tools/domain_builder/hypervisor_defs.h b/tools/domain_builder/hypervisor_defs.h new file mode 100644 index 0000000000..7d0aba03d7 --- /dev/null +++ b/tools/domain_builder/hypervisor_defs.h @@ -0,0 +1,36 @@ +/****************************************************************************** + * hypervisor_defs.h + * + * This needs to be kept in sync with Xen's pagetable update interface! + * + * Copyright (c) 2002-2003, Keir Fraser & Boris Dragovic + */ + +/* taken from include/hypervisor-ifs/hypervisor-if.h */ +typedef struct +{ +/* + * PGREQ_XXX: specified in least-significant bits of 'ptr' field. All requests + * specify relevent PTE or PT address in 'ptr'. Normal requests specify update + * value in 'value'. Extended requests specify command in least 8 bits of + * 'value'. + */ + unsigned long ptr, val; /* *ptr = val */ +} page_update_request_t; + +/* A normal page-table update request. */ +#define PGREQ_NORMAL 0 +#define PGREQ_MPT_UPDATE 1 +/* An extended command. */ +#define PGREQ_EXTENDED_COMMAND 2 +/* Announce a new top-level page table. */ +#define PGEXT_PIN_L1_TABLE 0 +#define PGEXT_PIN_L2_TABLE 1 +#define PGEXT_PIN_L3_TABLE 2 +#define PGEXT_PIN_L4_TABLE 3 +#define PGEXT_UNPIN_TABLE 4 +#define PGEXT_NEW_BASEPTR 5 +#define PGEXT_TLB_FLUSH 6 +#define PGEXT_INVLPG 7 +#define PGEXT_CMD_MASK 255 +#define PGEXT_CMD_SHIFT 8 diff --git a/tools/domain_builder/mem_defs.h b/tools/domain_builder/mem_defs.h new file mode 100644 index 0000000000..a9a1441d61 --- /dev/null +++ b/tools/domain_builder/mem_defs.h @@ -0,0 +1,45 @@ +/* + * memory related definitions needed for userspace domain builder dom0 application. these _need_ to + * be kept in sync with the kernel .h files they were copied over from or something horrible will + * happen. remmember: god kills a kitten every time you forget to keep these in sync. + * + * KAF: Boris, these constants are all fixed by x86 hardware. So the kittens are safe for now :-) + * + * Copyright 2002 by B Dragovic + */ + +/* copied over from hypervisor: include/asm-i386/page.h */ + +#define _PAGE_PRESENT 0x001 +#define _PAGE_RW 0x002 +#define _PAGE_USER 0x004 +#define _PAGE_PWT 0x008 +#define _PAGE_PCD 0x010 +#define _PAGE_ACCESSED 0x020 +#define _PAGE_DIRTY 0x040 +#define _PAGE_PAT 0x080 +#define _PAGE_PSE 0x080 +#define _PAGE_GLOBAL 0x100 + + +#define L1_PAGETABLE_SHIFT 12 +#define L2_PAGETABLE_SHIFT 22 + +#define ENTRIES_PER_L1_PAGETABLE 1024 +#define ENTRIES_PER_L2_PAGETABLE 1024 + +#define PAGE_SHIFT L1_PAGETABLE_SHIFT +#define PAGE_SIZE (1UL << PAGE_SHIFT) +#define PAGE_MASK (~(PAGE_SIZE-1)) + +typedef struct { unsigned long l1_lo; } l1_pgentry_t; +typedef struct { unsigned long l2_lo; } l2_pgentry_t; + +#define l1_table_offset(_a) \ + (((_a) >> L1_PAGETABLE_SHIFT) & (ENTRIES_PER_L1_PAGETABLE - 1)) +#define l2_table_offset(_a) \ + ((_a) >> L2_PAGETABLE_SHIFT) + +/* local definitions */ + +#define nr_2_page(x) (x << PAGE_SHIFT) diff --git a/tools/domain_builder/newdom b/tools/domain_builder/newdom new file mode 100644 index 0000000000..979661ec26 --- /dev/null +++ b/tools/domain_builder/newdom @@ -0,0 +1,17 @@ +#!/bin/sh + +VIFINIT=./vifinit +DOM_BUILDER=./domain_builder + +DOM=$2 + +ADDR=`/sbin/ifconfig eth0 | grep inet.addr | sed -e 's/.*inet addr:\([0-9.]*\) .*/\1/'` + +LO=`echo $ADDR | sed -e 's/[0-9]\+\.[0-9]\+\.[0-9]\+\.\([0-9]\+\)/\1/'` +HI=`echo $ADDR | sed -e 's/\([0-9]\+\.[0-9]\+\.[0-9]\+\)\.[0-9]\+/\1/'` + +NEWADDR=$HI.$[LO+DOM] + +$VIFINIT $DOM $NEWADDR + +$DOM_BUILDER 16000 $1 1 diff --git a/tools/domain_builder/vifinit b/tools/domain_builder/vifinit new file mode 100644 index 0000000000..58b50eaab0 --- /dev/null +++ b/tools/domain_builder/vifinit @@ -0,0 +1,25 @@ +#!/bin/bash +# +# vifinit +# +# This is a silly little script to dump a couple of simple rules down to +# the hypervisor to assign a full static IP to a given virtual interface. +# +# Usage is: +# +# vifinit [vif id] [dotted decimal ip address] +# +if [ $# -ne 2 ] ; +then + echo "usage: vifinit [vif id] [dotted decimal ip address]" + exit +fi + +#outbound rule: +echo "ADD ACCEPT srcaddr=$2 srcaddrmask=255.255.255.255 srcint=$1 dstint=-1 proto=any" > /proc/vfr + +#inbound rule: +echo "ADD ACCEPT dstaddr=$2 dstaddrmask=255.255.255.255 srcint=-4 dstint=$1 proto=any" > /proc/vfr + +#----] done. + diff --git a/xen-2.4.16/arch/i386/apic.o b/xen-2.4.16/arch/i386/apic.o new file mode 100644 index 0000000000000000000000000000000000000000..deb15b2737fc1eb39bb901e166a2c532027afce2 GIT binary patch literal 58292 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